package com.gh.lucene.util.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;

import com.gh.lucene.util.UserData;
import com.gh.lucene.util.mapping.SearchMapper;
import com.gh.lucene.util.search.query.RangeCollector;

public class ScoreSortedQuerySession implements QuerySession {
	private Query query;
	private Filter filter;
	private SearchWorker worker;
	private SearchMapper searchMapper;

	private Queue<UserData> userDatas = new LinkedList<UserData>();
	private int cursor = 0;
	private TopDocs topDocs = null;
	RangeCollector collector = RangeCollector.createCollector();

	public ScoreSortedQuerySession(Query query, SearchWorker worker,
			SearchMapper searchMapper) {
		super();
		this.query = query;
		this.worker = worker;
		this.searchMapper = searchMapper;
	}

	public ScoreSortedQuerySession(Query query, Filter filter,
			SearchWorker worker, SearchMapper searchMapper) {
		super();
		this.query = query;
		this.filter = filter;
		this.worker = worker;
		this.searchMapper = searchMapper;
	}

	@Override
	public void close() {

	}

	@Override
	public void execute() throws SearchException {

		if (filter == null) {
			try {
				worker.excuteQuery(query, collector);
				topDocs = collector.topDocs();
			} catch (IOException e) {
				e.printStackTrace();
				throw new SearchException(e);
			}
		} else {
			try {
				worker.excuteQuery(query, filter, collector);
				topDocs = collector.topDocs();
			} catch (IOException e) {
				e.printStackTrace();
				throw new SearchException(e);
			}
		}

		tryEnsureHasOne();
	}

	@Override
	public boolean hasMore() {
		return userDatas.size() > 0;
	}

	@Override
	public UserData next() throws SearchException {
		UserData userData = userDatas.poll();
		tryEnsureHasOne();
		return userData;
	}

	@Override
	public List<UserData> next(int length) throws SearchException {
		tryEnsureHasMore(length);
		List<UserData> datas = new ArrayList<UserData>(length);
		while (datas.size() < length && userDatas.size() > 0) {
			datas.add(userDatas.poll());
		}
		tryEnsureHasOne();
		return datas;
	}

	private void tryEnsureHasOne() throws SearchException {
		tryEnsureHasMore(1);
	}

	private void tryEnsureHasMore(int howMany) throws SearchException {
		while (userDatas.size() < howMany && cursor < topDocs.totalHits) {
			int index = cursor;
			cursor++;
			int docId = topDocs.scoreDocs[index].doc;
			try {
				UserData userData = searchMapper.searchMap(worker.get(docId));
				if (userData != null) {
					userDatas.add(userData);
				}
			} catch (CorruptIndexException e) {
				e.printStackTrace();
				throw new SearchException(e);
			} catch (IOException e) {
				e.printStackTrace();
				throw new SearchException(e);
			}
		}
	}

}
